fix(cli): bind transparent agent runs to gateway credentials#449
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (1)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (8)**/*.rs📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Files:
**/*.{rs,py}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{rs,py,js,mjs,cjs,ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{rs,py,go,js,ts,c,h}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.{rs,go,js,ts}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Files:
**/*.{rs,py,go,js,ts}📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Files:
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}⚙️ CodeRabbit configuration file
Files:
🔇 Additional comments (1)
WalkthroughTransparent runs generate invocation-bound proxy credentials, deliver them through Claude, Codex, and Hermes configuration, consume them at gateway ingress, and propagate structured authorization through routing and upstream forwarding. Tests cover credential handling, redaction, dispatch policies, and HTTP 401 rejection. ChangesTransparent proxy authentication
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Agent
participant TransparentGateway
participant TargetBinding
participant Provider
Agent->>TransparentGateway: Send invocation and provider credentials
TransparentGateway->>TransparentGateway: Consume invocation credential
TransparentGateway->>TargetBinding: Resolve source or explicit target route
TargetBinding->>Provider: Forward sanitized request with selected auth
Provider-->>TransparentGateway: Return upstream response
TransparentGateway-->>Agent: Return response
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
5074b8f to
235ef2d
Compare
Design walkthrough 1/3: invocation credential lifecycleThe proxy credential authenticates the child process to the exact transient gateway invocation that launched it. It is not a provider credential and is consumed before Relay runs any request or routing logic. sequenceDiagram
autonumber
participant CLI as nemo-relay run
participant Launch as PreparedAgentLaunch
participant Agent as Hermes / Codex / Claude Code
participant Ingress as Transparent gateway ingress
participant Pipeline as Relay intercept and dispatch pipeline
CLI->>Launch: Prepare one transparent agent invocation
Launch->>Launch: Generate random 256-bit proxy credential
Launch->>Agent: Child-only environment and harness configuration
Launch->>Ingress: Store expected credential for this gateway instance
Agent->>Ingress: Provider request plus proxy credential
Ingress->>Ingress: Extract supported credential carrier
Ingress->>Ingress: Compare in constant time
alt Credential missing or foreign
Ingress-->>Agent: HTTP 401 Unauthorized
Note over Ingress,Pipeline: Body decoding, interceptors, routing, and upstream dispatch never run
else Credential matches this invocation
Ingress->>Ingress: Consume proxy credential
Ingress->>Pipeline: Request plus typed source-credential provenance
Pipeline-->>Agent: Normal provider response
end
CLI->>Agent: Redact secret environment values from --print output
Security properties:
|
Design walkthrough 2/3: one gateway contract, harness-specific deliveryRelay owns one authentication contract while each harness uses its supported configuration surface. This avoids hard-coding a private placeholder from any agent implementation into the gateway. flowchart LR
R["Per-run proxy credential"] --> E["Child-only NEMO_RELAY_PROXY_CREDENTIAL"]
E --> HCFG["Hermes isolated model overlay"]
E --> CCFG["Codex env_http_headers"]
E --> ACFG["Claude Code ANTHROPIC_CUSTOM_HEADERS"]
subgraph Hermes["Hermes transport"]
HCFG --> HAUTH["Standard custom-provider API-key carrier"]
end
subgraph Codex["Codex transport"]
CCFG --> CHDR["x-nemo-relay-proxy-token"]
CPROV["Independent provider Authorization, if present"] --> CREQ["Codex request"]
CHDR --> CREQ
end
subgraph Claude["Claude Code transport"]
ACFG --> AHDR["x-nemo-relay-proxy-token"]
ACUSTOM["Existing custom headers"] --> AREQ["Claude request"]
AHDR --> AREQ
end
HAUTH --> G["Shared transparent-gateway validator"]
CREQ --> G
AREQ --> G
G --> N["Normalized authenticated source request"]
New harnesses only need to deliver the per-run value using a supported child configuration mechanism. They do not require new credential literals or harness-specific branches in gateway authentication. |
Design walkthrough 3/3: provider authentication ownership after routingInvocation authentication ends at ingress. Provider authentication is then resolved according to who owns the final destination. flowchart TD
A["Authenticated source request"] --> B["Record source credential disposition"]
B --> C["Run request interceptors"]
C --> D{"Explicit target dispatch?"}
D -- "No" --> S1["Keep source route"]
S1 --> S2["Preserve legitimate source credential"]
S2 --> S3["Apply existing ambient provider-key behavior when needed"]
S3 --> U1["Dispatch to original upstream"]
D -- "Yes" --> T1["Consume Relay internal route and URL headers"]
T1 --> T2["Remove all source provider credential carriers"]
T2 --> T3["Disable ambient environment-key injection"]
T3 --> T4["Apply Relay-owned target-binding headers"]
T4 --> E{"Target binding supplies auth?"}
E -- "Yes" --> U2["Dispatch with binding-owned target credential"]
E -- "No" --> U3["Dispatch to intentionally keyless target without auth"]
U1 --> R["Response translation and Relay lifecycle"]
U2 --> R
U3 --> R
This produces consistent outcomes across provider combinations:
The important boundary is that plugins select a target but do not inherit authentication accidentally. Relay consumes its internal dispatch headers, strips source ownership, and applies only the credentials configured for the selected destination. |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/cli/src/agents/claude/launch.rs`:
- Around line 24-31: Update the custom-header construction in the Claude launch
flow to remove all existing case-insensitive x-nemo-relay-proxy-token entries
before appending proxy_header, ensuring the newly appended token is the first
matching value. Add a regression test covering ANTHROPIC_CUSTOM_HEADERS that
already contains this header and verify only the current proxy token is used.
In `@crates/cli/tests/coverage/agents/launcher_tests.rs`:
- Around line 766-780: Create an EnvScope for ANTHROPIC_CUSTOM_HEADERS and clear
it before constructing the launch in this test, ensuring the scope remains
active through the assertions. Keep the existing custom-header and
secret-environment checks unchanged so the test verifies cross-request isolation
without inheriting process-wide state.
In `@crates/cli/tests/coverage/shared/gateway_tests.rs`:
- Around line 1128-1146: Update
transparent_proxy_rejects_missing_or_foreign_credentials to match each consume
failure against CliError::Unauthorized(_) rather than only asserting is_err(),
and convert the error through IntoResponse to assert StatusCode::UNAUTHORIZED.
Preserve the existing check that foreign credentials remain unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: ea940f23-fc7a-4e5d-b6e3-cecfd37944f3
📒 Files selected for processing (21)
crates/cli/src/agents/claude/launch.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/error.rscrates/cli/src/gateway/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/gateway/response.rscrates/cli/src/gateway/routes.rscrates/cli/src/lib.rscrates/cli/src/process/launcher.rscrates/cli/src/process/prepared.rscrates/cli/src/provider_auth.rscrates/cli/src/server/mod.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/server_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
**/*.rs
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node workWhen changing the core Rust runtime or Rust-facing API surface, format Rust code with
cargo fmt(rustfmt defaults), keepcargo clippy -- -D warningsclean, and satisfycargo deny checkperdeny.toml.
**/*.rs: If any Rust code changed, always runjust test-rust.
If any Rust code changed, also runcargo fmt --all.
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, runcargo fmt --allandcargo clippy --workspace --all-targets -- -D warningseven if relying on pre-commit.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/src/gateway/response.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
**/*.{rs,py}
📄 CodeRabbit inference engine (AGENTS.md)
Follow binding naming conventions in Rust and Python: use
snake_case.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/src/gateway/response.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,py,js,mjs,cjs,ts,tsx}: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/src/gateway/response.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
**/*.{rs,py,go,js,ts,c,h}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Use language-appropriate naming conventions: Rust
snake_case, C FFI exports prefixednemo_relay_, GoPascalCase, Node.jscamelCase, and Pythonsnake_case.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/src/gateway/response.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
**/*.{rs,go,js,ts}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding
//comment form.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/src/gateway/response.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
{crates/**/src/**/*.rs,python/**/*.py}
📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)
Do not add tests under
src; Rust tests belong in cratetests/trees, and Python SDK tests belong underpython/tests.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/src/gateway/response.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
**/*
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, usemaintain-dynamic-pluginsand include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, preferuv run pre-commit run --files <changed files...>.
Before review or handoff, runuv run pre-commit run --all-files.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/src/gateway/response.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
**/*.{rs,py,go,js,ts}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If a language surface changed, always run that language's test target even when Rust core did not change.
Files:
crates/cli/src/lib.rscrates/cli/src/error.rscrates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/src/gateway/response.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/src/agents/shared/alignment.rscrates/cli/src/gateway/routes.rscrates/cli/src/process/prepared.rscrates/cli/src/agents/codex/launch.rscrates/cli/src/provider_auth.rscrates/cli/src/agents/claude/launch.rscrates/cli/src/agents/hermes/config.rscrates/cli/src/agents/mod.rscrates/cli/src/gateway/request.rscrates/cli/src/process/launcher.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rscrates/cli/src/server/mod.rscrates/cli/src/gateway/mod.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}
⚙️ CodeRabbit configuration file
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.
Files:
crates/cli/tests/coverage/shared/mcp_tests.rscrates/cli/tests/coverage/shared/installer_tests.rscrates/cli/tests/coverage/agents/hermes_tests.rscrates/cli/tests/coverage/shared/gateway_tests.rscrates/cli/tests/coverage/shared/server_tests.rscrates/cli/tests/coverage/agents/launcher_tests.rs
🔇 Additional comments (26)
crates/cli/tests/coverage/agents/hermes_tests.rs (1)
1161-1168: 📐 Maintainability & Code QualityRun the required Rust validation and confirm error-path coverage.
These assertions correctly match the transparent Hermes configuration contract. Before handoff, run
just test-rust,cargo fmt --all,cargo clippy --workspace --all-targets -- -D warnings, anduv run pre-commit run --all-files. Also confirm that the suite covers invalid or missing model configuration paths, not only successful rewriting.As per coding guidelines, Rust changes require the listed formatting, lint, test, and pre-commit checks. As per path instructions, tests should cover the changed API behavior, including error paths.
Sources: Coding guidelines, Path instructions
crates/cli/src/lib.rs (1)
22-22: 📐 Maintainability & Code QualityConfirm the required Rust validation passes.
Please run
cargo fmt --all,cargo clippy --workspace --all-targets -- -D warnings,just test-rust, anduv run pre-commit run --all-filesbefore handoff.As per coding guidelines, “Any Rust change must run
just test-rust,”cargo fmt --all, andcargo clippy --workspace --all-targets -- -D warnings.Source: Coding guidelines
crates/cli/src/provider_auth.rs (1)
1-42: LGTM!Also applies to: 44-86, 88-107, 109-168
crates/cli/src/error.rs (1)
37-38: LGTM!Also applies to: 103-103
crates/cli/src/agents/hermes/config.rs (1)
67-73: LGTM!crates/cli/tests/coverage/agents/launcher_tests.rs (1)
290-308: LGTM!Also applies to: 783-816, 905-912, 1742-1745
crates/cli/tests/coverage/shared/mcp_tests.rs (1)
424-424: LGTM!crates/cli/tests/coverage/shared/gateway_tests.rs (1)
94-106: LGTM!Also applies to: 406-406, 424-545, 572-572, 607-607, 720-723, 914-917, 948-960, 1038-1126, 1374-1374, 1411-1411
crates/cli/src/process/launcher.rs (4)
148-155: LGTM!
321-336: LGTM!
406-433: LGTM!
547-554: LGTM!crates/cli/src/agents/mod.rs (1)
386-397: LGTM!crates/cli/src/agents/codex/launch.rs (1)
199-205: LGTM!crates/cli/tests/coverage/shared/server_tests.rs (4)
397-397: LGTM!Also applies to: 505-505, 573-573
430-493: LGTM!
2408-2492: LGTM!
3009-3011: LGTM!crates/cli/tests/coverage/shared/installer_tests.rs (1)
60-67: 🎯 Functional CorrectnessConfirm the credential reaches
send_verified_hook_forward_request. If that helper does not inject the generated token/header, this test will 401 before exercising the transparent ingress path.crates/cli/src/process/prepared.rs (1)
6-27: 📐 Maintainability & Code QualityRun the Rust validation suite before handoff.
cargo fmt --all,cargo clippy --workspace --all-targets -- -D warnings,just test-rust,cargo deny check, anduv run pre-commit run --all-filesare still required.crates/cli/src/agents/shared/alignment.rs (1)
549-549: LGTM!crates/cli/src/server/mod.rs (1)
60-61: LGTM!Also applies to: 83-83, 104-110, 221-239, 257-288, 436-463, 480-511
crates/cli/src/gateway/routes.rs (1)
18-32: LGTM!crates/cli/src/gateway/request.rs (1)
24-40: LGTM!Also applies to: 55-69, 74-84
crates/cli/src/gateway/mod.rs (1)
66-72: LGTM!Also applies to: 119-134, 243-255, 426-437, 688-728, 733-744, 763-846, 949-960, 1085-1105
crates/cli/src/gateway/response.rs (1)
59-71: LGTM!
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
|
/ok to test 5f3780d |
|
/merge |
Overview
Bind every transparent
nemo-relay run --agent ...invocation to a fresh gateway credential, and make provider-authentication ownership explicit when plugins select a different upstream.The original failure surfaced through Hermes, but interpreting Hermes's
Bearer no-key-requiredliteral inside the generic gateway would have coupled Relay to one harness implementation and left cross-provider credential ownership ambiguous. This PR instead establishes one provider- and harness-neutral boundary:No literal
Bearer no-key-requiredbehavior remains in Relay.Design
Invocation-bound gateway authentication
NEMO_RELAY_PROXY_CREDENTIALand the harness's supported configuration mechanism.--printoutput.Hermes, Codex, and Claude Code share this gateway contract while retaining their native configuration surfaces. A harness that supports a dedicated header may keep a separate provider credential alongside the proxy credential.
Explicit provider-auth ownership
The gateway records typed source-credential provenance before routing and resolves credentials based on the final destination:
This prevents a source
Authorization,x-api-key,api-key, oranthropic-api-keyvalue from accidentally becoming the credential for a plugin-selected destination.Detailed design walkthroughs
Compatibility
This is not a public API breaking change. It intentionally tightens only transparent agent gateways: provider routes accept requests from the child process launched for that invocation. Foreground gateways and managed-sidecar proof behavior remain unchanged.
Validation
Local validation completed:
cargo check -p nemo-relay-cli --all-targetscargo fmt --all -- --checkcargo clippy -p nemo-relay-cli --all-targets -- -D warningscargo test -p nemo-relay-clicargo test -p nemo-relay-switchyardcargo build -p nemo-relay-cli --features switchyardFocused regressions cover:
Real-service validation completed against an authenticated OpenAI-compatible endpoint:
regex-logrun throughnemo-relay run --agent hermescompleted with reward1.0.Where should the reviewer start?
Start with
crates/cli/src/provider_auth.rs, which defines generation, comparison, supported carriers, and typed credential provenance. Then review:crates/cli/src/process/prepared.rsand the agent launchers for per-run delivery;crates/cli/src/server/mod.rsfor ingress enforcement; andcrates/cli/src/gateway/mod.rsfor source-versus-target authentication ownership.The HTTP-level regressions in
crates/cli/tests/coverage/shared/server_tests.rsand dispatch-policy regressions incrates/cli/tests/coverage/shared/gateway_tests.rsdescribe the intended behavior end to end.Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
Summary by CodeRabbit